feat: path URLs for the playground's components - #27
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every component in the playground now has an address.
flowui.stac.dev/playgroundwas one URL for eighteen views: selection lived insetState, so a component could not be linked, bookmarked, or reached with the back button. This puts the component in the path.go_routerwith a single/:componentroute. Paths are the item's slug, the enum name in kebab-case:/playground/composer,/playground/full-chat,/playground/thinking-indicator.index.html?embed=<slug>&variant=&theme=works byte-for-byte, soFlowDemo.astro, the 37<FlowDemo>call sites and the?themepre-paint script needed no changes.mainbranches on?embed=before the app is built, so embeds never reach the router, andusePathUrlStrategy()is called after that branch on purpose: under path strategy the iframe's/playground/index.html?embed=…would read as the route name/index.html, which the embed's plain Navigator cannot build.Design notes worth a reviewer's eye:
pageBuilderreturns aNoTransitionPageunder a constantValueKey, soPage.canUpdatepasses and the Navigator updates the page in place rather than pushing._PlaygroundShellStatesurvives navigation, and with it the variant memory, the device toggle and the code panel — including across browser back and forward.ShellRoute. It would insert a nested Navigator and Overlay exactly where the stage sits, soFlowMenuandFlowModelSelectorpopovers would clip to the stage pane instead of the window. The phone stage's own nested Navigator stays untouched.PlaygroundItem, andembed.dartnow uses the same getter. The path and the embed id are the same string by construction, so they cannot drift./, a typo, or a stale link all redirect to/full-chatin one top-level rule, so there is no reachable error page and no nullable item threading through the shell.Hosting
Component paths are routes, not files, so they need a rewrite.
docs/public/_redirectsadds one line per slug. Two details are load-bearing, both learned fromwrangler pages devrather than guessed, and both recorded in the file's comment:/playground/*is rejected by Pages outright ("infinite loop detected", rule ignored, deep links 404) because it strips.htmland/indexfrom the destination and then sees it match the source again./playground/index.html. Naming index.html makes Pages normalise it into a 308 to/playground/, which throws the component away and lands every deep link on the default.The dev-server equivalent extends the existing
playground-dev-indexintegration inastro.config.mjs, matching extensionless paths only somain.dart.js,canvaskit/andassets/pass through.Screenshots
How this was verified
flutter analyzeclean in the package and the playground,dart format --set-exit-if-changedclean, andnpm run build:siteproduces the same wasm build the deploy runs, with_redirectsand<base href="/playground/">indist/.Against a real
wrangler pages dev dist, so the rules are exercised the way Cloudflare will run them: all 18 rules parse; deep links return 200 and serve the app with the URL preserved;main.dart.wasm,flutter_bootstrap.js,main.dart.jsandassets/still resolve; COOP and COEP survive the rewrite; both embed forms still boot;/playground/and the docs pages are unaffected. Headless captures confirm/playground/markdownand/playground/pillstage the right component with the sidebar and code panel in step.Checklist
flutter analyze libandflutter analyzeinexample/andplayground/are cleandart format .applieddependencies:inpubspec.yaml(Flutter SDK and flutter.dev packages only)lib/flow_ui.dartand documented indocs/and the README tableCHANGELOG.mdupdated for user-facing changes, with breaking changes called outfeat:,fix:,refactor:,docs:,chore:)Note
Medium Risk
Production deep links depend on per-slug Cloudflare
_redirectsand dev middleware staying in sync when components are added; embed routing order is load-bearing but scoped to the playground/docs site, not the core package.Overview
Playground components are now addressable by path (e.g.
/playground/composer,/playground/full-chat) instead of living only in shellsetState, so links, bookmarks, and the back button work. The app usesgo_routerwith a single/:componentroute,usePathUrlStrategy()(after the embed branch), andPlaygroundItem.slugshared with embed parsing so path and?embed=ids stay aligned. Variant, theme, and device stay in shell state; sidebar selection callscontext.go.The
?embed=iframe contract is unchanged —mainstill bootsEmbedAppbefore the router. Docs hosting addsdocs/public/_redirects(one explicit rule per slug for Cloudflare Pages) and extends the Astroplayground-dev-indexmiddleware for extensionless component paths in dev. READMEs document the two URL contracts;.wrangler/is gitignored for local redirect verification.Reviewed by Cursor Bugbot for commit 4ca609c. Bugbot is set up for automated code reviews on this repo. Configure here.